--[[
Name: BrownieHelper
Developed by: Sonora (The Dragon Flight)
Website: http://sonora.kirintor.googlepages.com/contents
SVN: http://code.google.com/p/browniehelper/source/browse
License: GNU General Public License v3
]]

--[[
Collection of widgets that provide a simplified API for doing common UI tasks.

See comments below for usage examples.

Including the associated widets.xml is sufficient to expose this library.
]]--

local __version__ = "1.0"

--Constructor functionality
local new = function(class, frame)
    --inheritence with out metatable mutation... (ugly? but good. this is lua.)
    for k,v in pairs(class) do frame[k] = v end
    if class.OnLoad~=nil then frame:OnLoad() end
end

--Widgets
wiDropdownBox = {
    --[[
    Simple drop down box with easy to use API (looks like drop down or
    Zone/Guild/Race on the who tab of the social window.)
    
    Example usage:
    
    ~In XML~
    <Frame name="myDropdown" inherits="wiDropdownBox" />
    
    ~In Lua~
    --Add some options, with the default (defaults to first option)
    options = {"A","B","C"} --options are just a list of strings
    myDropdown:setOptions(options, "B") --Sets "B" as the initially selected option
    
    
    ]]--
    new=new,
    
    --Attributes
    wiOptions = {"none"},
    
    --API
    setOptions = function(self, options, default)
        self.wiOptions = options
        self.wiDropdownSelection = (default or options[1])
        self:OnLoad()
    end,
    
    getSelected = function(self) return UIDropDownMenu_GetSelectedValue(self) end,
    setSelected = function(self, option)
        --option should be a member of options from setOptions(options), or a valid index on options; or nil to default to selecting first option
        if option==nil then self.wiDropdownSelection = self.wiOptions[1]
        else
            isinoptions, index = has(self.wiOptions, option)
            if isinoptions then --if its a member of self.wiOptions
                self.wiDropdownSelection = option
            else
                self.wiDropdownSelection = self.wiOptions[option]
            end
        end
        
        self:OnLoad()
        
    end,
    
    registerSelectionCallback = function(self, callback)
        self.selectionCallback = callback
    end,
    
    --Internals
    Initialize = function(self)
        local info = UIDropDownMenu_CreateInfo()
	for idx,val in pairs(self.wiOptions) do
            
            info.text = val
            info.value = val
            info.func = function() self:OnDropDownClick(this) end
            info.owner = self
            info.checked = (val==self.wiDropdownSelection)
            
            UIDropDownMenu_AddButton(info)
            
	end
        
        --Set a default selection
        UIDropDownMenu_SetSelectedValue(self, self.wiDropdownSelection)
        
    end,
    
    OnLoad = function(self)
        UIDropDownMenu_Initialize(self, function() self:Initialize() end)
    end,
    
    OnDropDownClick = function(self, info)
	UIDropDownMenu_SetSelectedValue(info.owner, info.value)
        self.wiDropdownSelection = info.value
        if self.selectionCallback then self.selectionCallback(info.value) end
    end,
    
}

wiScrollingTable = {
    --[[
    Offers a scrolling, sortable table displaying fields defined on a sequence
    of row objects.
    
    Example usage:
    
    ~In XML~
    <ScrollFrame name="myFrame" inherits="wiScrollingTableTemplate" />
    
    ~In Lua~
    --Add some fields
    myFrame:setFields({"Field1","Field2","Field3"})
    
    --Add some rows
    myFrame:addRow({Field1="1A", Field2="1B", Field3="1C"})
    myFrame:addRow({Field1="2A", Field2="2B", Field3="2C"})
    
    --If you mutate row data in place, call updateView()
    local row = {Field1="2A", Field2="2B", Field3="2C"}
    myFrame:addRow(row)
    row.Field1 = "something new"
    myFrame:updateView()
    
    --Delete the row
    myFrame:deleteRow(row)
    
    --Setting selection mode
    myFrame:multiselect = true --user can select multiple rows
    myFrame:miltiselect = false --user can only select one row
    
    --Setting the selection programatically
    myFrame:setSelection(row)
    
    --Getting the selected row
    selected_rows = myFrame:getSelections()
    for i,selected_row in pairs(selected_rows) do ... end
    
    if row.wiTableSelected==true then ... end --alternatively, you can check the selected flag on a row table directly, just remember to call updateView if you mutate this attribute (will not unselect other selected rows even if multiselect==false)
    
    --If you want to know when a row was selected or unselected...
    f = function(row,selected)
        --row is the row table that got hit with the selection event
        --selected == true if the row is now selected, false if it is not
    end
    myFrame:registerSelectionCallback(f)
    
    ]]--
    
    new = new,
    
    --Attributes
    button_height = 16,
    widths = nil,
    draw_y_offset = 0,
    draw_x_offset = 8,
    multiselect = false,
    candeselect = true, --is it possible to have nothing selected once the list is non-empty
    
    --API
    setFields = function(self, fields)
        --fields should be an orderd list of field names: {"field1", "field2", ...}
        if self.fields~=nil then
            error("Warning: you cannot set the fields more than once in the life time of a table.")
        elseif fields~=nil and #fields>0 then
            self.fields = fields
            self.sorting = fields[1]
            self.ascending=true
            self:drawButtons()
        else
            error("Warning: 'fields' argument must be non nil and not empty.")
        end
    end,
    
    setWidths = function(self, widths)
        --widths should be the same length as fields
        self.widths = widths
    end,
    
    addRow = function(self, row)
        table.insert(self.rows, row)
        self:updateView()
    end,
    
    deleteRow = function(self, row)
        for i, r in pairs(self.rows) do
            if row==r then table.remove(self.rows,i) end
        end
        self:updateView()
    end,
    
    clearRows = function(self)
        self.rows = {}
        self:updateView()
    end,
    
    getRows= function(self)
        --returns ordered rows, based on current sorting
        
        --We can only return rows if sorting is properly set
        if self.sorting==nil then return {} end
        
        --Sort rows in place by comparing the value of the sorting parameter
        if self.ascending then
            table.sort(self.rows, function(a,b) return a[self.sorting] < b[self.sorting] end)
        else
            table.sort(self.rows, function(a,b) return a[self.sorting] > b[self.sorting] end)
        end
        
        --Just return rows now
        return self.rows
        
    end,
    
    updateView = function(self)
        self:OnShow()
    end,
    
    getSelections = function(self)
        selected_rows = {}
        for i,row in pairs(self.rows) do
            if row.wiTableSelected==true then table.insert(selected_rows, row) end
        end
        return selected_rows
    end,
    
    setSelection = function(self, row, selected)
        if selected==nil then selected=not row.wiTableSelected end
        
        --Row should be a row that we've seen with addRow
        if self.candeselect or selected then
            
            row.wiTableSelected = selected
            if selected and self.multiselect==false then
                for i,other_row in pairs(self.rows) do
                    if row~=other_row then other_row.wiTableSelected=false end
                end
            end
            self:updateView()
            
            --If we have a selection callback
            if self.selectionCallback then self.selectionCallback(row,row.wiTableSelected) end
        end
        
    end,
    
    clearSelections = function(self)
        for i,row in pairs(self:getSelections()) do self:setSelection(row, false) end
    end,
    
    registerSelectionCallback = function(self, callback)
        --calls callback(row,selected)
        self.selectionCallback=callback
    end,
    
    registerDoubleClickCallback = function(self, callback)
        --Gets called when a row is double clicked
        self.doubleClickCallback = callback
    end,
    
    registerRowEnterCallback = function(self, callback)
        self.rowEnterCallback = callback
    end,
    
    registerRowLeaveCallback = function(self, callback)
        self.rowLeaveCallback = callback
    end,
    
    --Internals    
    drawButtons = function(self)
        
        self.wiTableButtons = {}
        local header_buttons = {}
        
        if self.widths == nil then
            self.widths = {}
            for i=1,#self.fields do table.insert(self.widths,self:GetWidth()/#self.fields) end
        end
        
        --create a row of buttons that will be our header
        for i,field in pairs(self.fields) do
            local newButt = CreateFrame("Button", self:GetName().."HeaderButton"..field, self:GetParent(), "wiTableHeaderButtonTemplate")
            newButt:SetHeight(self.button_height)
            newButt:SetWidth(self.widths[i])
            newButt:SetText(field)
            newButt:SetScript("OnClick", function() PlaySound("igMainMenuOptionCheckBoxOn"); self:OnSortButton(field); end)
            
            if i==1 then
                newButt:SetPoint("TOPLEFT", self, "TOPLEFT", self.draw_x_offset, self.draw_y_offset)
            else
                newButt:SetPoint("TOPLEFT", header_buttons[#header_buttons], "TOPRIGHT")
            end
            table.insert(header_buttons, newButt)
            
        end
        
        --create enough TableButtons to fill the current window size
        for row=1,(self:GetHeight()-self.button_height)/self.button_height do --rows of buttons
            
            local newButt = CreateFrame("Button", self:GetName().."ButtonRow"..tostring(row), self:GetParent(), "wiTableButtonTemplate")
            newButt:SetHeight(self.button_height)
            newButt:SetWidth(self:GetWidth())
            newButt:SetScript("OnClick", function(button, event) self:OnSelection(button,event) end)
            newButt:SetScript("OnDoubleClick", function(button, event) self:OnDoubleClick(button,event) end)
            newButt:SetScript("OnEnter", function(button, event) self:OnEnter(button,event) end)
            newButt:SetScript("OnLeave", function(button, event) self:OnLeave(button,event) end)
            
            if row==1 then
                newButt:SetPoint("TOPLEFT", header_buttons[1], "BOTTOMLEFT")
            else
                newButt:SetPoint("TOPLEFT", self.wiTableButtons[#self.wiTableButtons], "BOTTOMLEFT")
            end
            
            newButt.fontStrings = {}
            for col,field in pairs(self.fields) do --columns of entries
                newButt.fontStrings[field] = newButt:CreateFontString(self:GetName().."FontString"..tostring(row)..tostring(field),"BACKGROUND","wiTableButtonFontStringTemplate")
                newButt.fontStrings[field]:SetPoint("TOP", newButt, "TOP")
                newButt.fontStrings[field]:SetPoint("LEFT", header_buttons[col], "LEFT")
                newButt.fontStrings[field]:SetWidth(header_buttons[col]:GetWidth())
                newButt.fontStrings[field]:SetHeight(self.button_height)
            end
            
            self.wiTableButtons[row] = newButt
            
        end
        self:OnShow()
        
    end,
    
    --Event handlers
    OnLoad = function(self, event)
        self.rows = {}
    end,
    
    OnShow = function(self, event)
        local line -- 1 through #button rows of our window to scroll
        local lineOffset -- an index into our data calculated from the scroll offset
        
        local rows = self:getRows()
        
        if rows==nil then return false end
        if self.wiTableButtons==nil then return false end
        
        FauxScrollFrame_Update(self, #self.rows, #self.wiTableButtons, self.button_height) --FauxScrollFrame_Update(frame,numItems,numToDisplay,valueStep,button,smallWidth,bigWidth,highlightFrame,smallHighlightWidth,bigHighlightWidth )
        
        for line=1,#self.wiTableButtons do
            
            lineOffset = line + (FauxScrollFrame_GetOffset(self) or 0)
            
            for col,field in pairs(self.fields) do
                local button = self.wiTableButtons[line]
                
                if lineOffset <= #self.rows then
                    local row = self.rows[lineOffset]
                    local txt = tostring(row[field])
                    
                    if row.wiTableSelected==true then button:LockHighlight()
                    else button:UnlockHighlight()
                    end
                    
                    button.row = row
                    button.fontStrings[field]:SetText(txt)
                    button:Show()
                else
                    button:Hide()
                end
            end
        end
    end,
    
    OnEnter = function(self, button)
        if self.rowEnterCallback~=nil then self.rowEnterCallback(button.row) end
    end,
    
    OnLeave = function(self, button)
        if self.rowLeaveCallback~=nil then self.rowLeaveCallback(button.row) end
    end,
    
    OnVerticalScroll = function(self, event)
        FauxScrollFrame_OnVerticalScroll(self.button_height, function() self:OnShow() end)
    end,
    
    OnDoubleClick = function(self, button)
        if self.doubleClickCallback~=nil then self.doubleClickCallback(button.row) end
    end,
    
    OnSortButton = function(self, field)
        if self.sorting==field then self.ascending = not self.ascending
        else self.sorting=field
        end
        self:OnShow()
    end,
    
    OnSelection = function(self, button, event)
        PlaySound("igMainMenuOptionCheckBoxOn")
        self:setSelection(button.row)
    end,
    
}


